package r25.engine;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;

import r25.board.IBoard;
import r25.character.ICharacter;
import r25.room.IRoom;
import r25.settings.SettingsManager;
import r25.tools.JsonHelper;
import r25.tools.LoggerHelper;
import r25.tools.ParameterHelper2;

public class GameService implements IGameService {
	private static final Logger logger = LoggerHelper.getLogger();

	private static final String DATA_CONF_KEY = "data";
	private static final String COMPOSITION_ROOMS_CONF_KEY = "compositionRooms";
	private static final String COMPOSITIONS_CONF_KEY = "compositions";
	private static final String BOARD_CONF_KEY = "board";
	private static final String CHARACTERS_CONF_KEY = "characters";

	private Map<String, Object> boardDef = new HashMap<String, Object>();
	private final Map<String, ICharacter> characterDefs = new HashMap<String, ICharacter>();
	private final Map<String, IRoom> roomDefs = new HashMap<String, IRoom>();
	private final Map<String, Composition> roomComps = new HashMap<String, Composition>();

	@Override
	public void release() {
		roomDefs.clear();
		roomComps.clear();
		characterDefs.clear();
	}

	@SuppressWarnings("unchecked")
	@Override
	public void configure(Map<Object, Object> map) throws Exception {
		release();

		String wDesc = (String) map.get(DATA_CONF_KEY);
		Map<String, Object> gameSetup;
		if (wDesc != null && wDesc.startsWith("/")) {
			gameSetup = JsonHelper.loadJson(SettingsManager.class
					.getResourceAsStream(wDesc));
		} else {
			gameSetup = JsonHelper.loadJson(new FileInputStream(wDesc));
		}

		Map<String, Object> rawRoomComps = (Map<String, Object>) gameSetup
				.get(COMPOSITIONS_CONF_KEY);
		for (Entry<String, Object> e : rawRoomComps.entrySet()) {
			roomComps.put(e.getKey(), new Composition(e.getKey(),
					(Map<String, Object>) e.getValue()));
		}

		List<Map<String, Object>> rawListDefs = (List<Map<String, Object>>) gameSetup
				.get(COMPOSITION_ROOMS_CONF_KEY);
		for (Map<String, Object> rrd : rawListDefs) {
			IRoom r = (IRoom) ParameterHelper2.newInstance(rrd);
			roomDefs.put(r.getName(), r);
			logger.debug("Defined room: {} -> {}", r.getName(), r);
		}
		logger.trace("Defined rooms: {}", roomDefs);

		boardDef = (Map<String, Object>) gameSetup.get(BOARD_CONF_KEY);
		logger.debug("Board: {}", boardDef);

		rawListDefs = (List<Map<String, Object>>) gameSetup
				.get(CHARACTERS_CONF_KEY);
		for (Map<String, Object> rrd : rawListDefs) {
			ICharacter r = (ICharacter) ParameterHelper2.newInstance(rrd);
			characterDefs.put(r.getName(), r);
			logger.trace("Available character: {} -> {}", r.getName(), r);
		}
		logger.debug("Available characters: {}", characterDefs);
	}

	@Override
	public List<IRoom> getCompositionRooms() {
		List<IRoom> rc = new ArrayList<IRoom>();
		rc.addAll(roomDefs.values());
		return rc;
	}

	@Override
	public List<ICharacter> getCompositionCharacters() {
		List<ICharacter> rc = new ArrayList<ICharacter>();
		rc.addAll(characterDefs.values());
		return rc;
	}

	@Override
	public List<String> getCompositions() {
		List<String> rc = new ArrayList<String>();
		rc.addAll(roomComps.keySet());
		return rc;
	}

	@Override
	public Composition getComposition(String name) {
		return roomComps.get(name);
	}

	@Override
	public IRoom getRoomInstance(String name) {
		IRoom r = roomDefs.get(name);
		if (r != null) {
			return r.newInstance();
		}
		return null;
	}

	@Override
	public IRoom pickRandomRoom(List<IRoom> l) {
		int i = (int) (Math.random() * l.size());
		IRoom rc = l.get(i);
		l.remove(i);
		return rc;
	}

	@Override
	public ICharacter pickRandomCharacter(List<ICharacter> l) {
		int i = (int) (Math.random() * l.size());
		ICharacter rc = l.get(i);
		l.remove(i);
		return rc;
	}

	@Override
	public IBoard setupBoardInstance(String name) throws Exception {
		IBoard rc = (IBoard) ParameterHelper2.newInstance(boardDef);

		Composition comp = getComposition(name);
		rc.setup(this, comp);

		return rc;
	}

	@Override
	public IGameInstance startGameInstance(String name, GAME_MODE mode,
			List<String> players) throws Exception {
		IGameInstance rc = new GameInstance(this, setupBoardInstance(name),
				mode, players);
		rc.start();

		return rc;
	}

	@Override
	public List<Integer> computePossibleNumberOfPlayers(GAME_MODE mode) {
		List<Integer> rc = new ArrayList<Integer>();
		rc.add(mode.getFromPlayers());
		if (!mode.isExactPlayers()) {
			for (int i = mode.getFromPlayers() + 1; i < mode.getToPlayers(); i++) {
				rc.add(i);
			}
		}
		if (mode.getToPlayers() > mode.getFromPlayers()) {
			rc.add(mode.getToPlayers());
		}

		return rc;
	}

	@Override
	public List<Integer> computePlayerDistribution(GAME_MODE mode, int np,
			int nc) {
		List<Integer> rc = new ArrayList<Integer>();

		int currentPlayer = 1;
		for (int c = 0; c < nc * np; c++) {
			rc.add(currentPlayer);
			currentPlayer++;
			if (currentPlayer > np) {
				currentPlayer = 1;
			}
		}

		return rc;
	}

	@Override
	public int computeNumberOfCharactersPerPlayer(GAME_MODE mode, int np) {
		int rc;

		switch (mode) {
		case COMPETITION:
			rc = 2;
			break;
		case COOPERATION:
			rc = (np < 4 ? 2 : 1);
			break;
		case SOLO:
			rc = 4;
			break;
		default:
			rc = 1;
			break;
		}

		return rc;
	}

	@Override
	public int computeNumberOfGuards(GAME_MODE mode, int np) {
		int rc = 0;
		if (GAME_MODE.SUSPICION.equals(mode)) {
			rc = (np == 4 ? 1 : 2);
		}

		return rc;
	}
}
